﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommonLibrary.Interfaces.Emailer;
using System.Net.Mail;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Net.Mime;
using System.IO;
using System.Net;

namespace CommonLibrary.Classes.Emailer
{
    public class EmailerBase : IEmailer
    {
        private SmtpClient _server;
        private MailAddress _fromAddress;
        private List<MailAddress> _toAddressList;
        private List<MailAddress> _ccAddressList;
        private List<MailAddress> _bccAddressList;
        private List<MailMessage> _mailMessageList;
        private MailMessage _currentMailMessage;
        private List<IEmailerSuccess> _resultList;

        public SmtpClient Server
        {
            get { return _server; }
        }

        public MailAddress FromAddress
        {
            get { return _fromAddress; }
        }

        public List<MailAddress> ToAddressList
        {
            get { return _toAddressList; }
        }

        public List<MailAddress> CCAddressList
        {
            get { return _ccAddressList; }
        }

        public List<MailAddress> BCCAddressList
        {
            get { return _bccAddressList; }
        }

        public List<MailMessage> MailMessageList
        {
            get { return _mailMessageList; }
        }

        public List<IEmailerSuccess> ResultsList
        {
            get { return _resultList; }
        }

        public MailMessage CurrentMessage
        {
            get
            {
                return _currentMailMessage;
            }
            set
            {
                _currentMailMessage = value;
            }
        }


        public bool AddEmailAddress(string emailAddress, Enumerations.EmailAddress emailAddressType, Encoding displayNameEncoder, string emailDisplayName = "")
        {
            bool valid = false;
            try
            {
                if (string.IsNullOrEmpty(emailDisplayName))
                {
                    emailDisplayName = emailAddress;
                }

                if (displayNameEncoder == null)
                {
                    displayNameEncoder = Encoding.UTF8;
                }


                switch (emailAddressType)
                {
                    case CommonLibrary.Enumerations.EmailAddress.ToAddress:
                        {
                            _toAddressList.Add(new MailAddress(emailAddress, emailDisplayName, displayNameEncoder));
                            break;
                        }
                    case CommonLibrary.Enumerations.EmailAddress.CCAddress:
                        {
                            _ccAddressList.Add(new MailAddress(emailAddress, emailDisplayName, displayNameEncoder));
                            break;
                        }
                    case CommonLibrary.Enumerations.EmailAddress.BCCAddress:
                        {
                            _bccAddressList.Add(new MailAddress(emailAddress, emailDisplayName, displayNameEncoder));
                            break;
                        }
                    case CommonLibrary.Enumerations.EmailAddress.FromAddress:
                        {
                            _fromAddress = new MailAddress(emailAddress, emailDisplayName, displayNameEncoder);
                            break;
                        }
                    default:
                        {
                            throw new InvalidOperationException(string.Format("Invalid Address Type passed in {0}", emailAddressType.ToString()));

                        }
                }

                valid = true;
            }
            catch (Exception error)
            {
                string emailTemplate = "Unable to add the email address: {0} with displayName: {1} and Address Type: {2}";
                throw new InvalidOperationException(string.Format(emailTemplate, emailAddress, emailDisplayName, emailAddressType.ToString()), error);
            }

            return valid;
        }

        public bool ValidateEmailAddress(string emailAddress, bool overrideEmailPatternCheck = false, string regexPattern = "")
        {
            bool valid = false;
            try
            {
                if (!overrideEmailPatternCheck)
                {
                    regexPattern = @"^(?("")(""[^""]+?""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
                                   @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9]{2,17}))$";
                }

                Regex validEmail = new Regex(regexPattern, RegexOptions.IgnoreCase);

                valid = validEmail.IsMatch(emailAddress);

            }
            catch (Exception error)
            {
                string emailTemplate = "Unable to pattern match the email Address: {0} using the regex pattern {1}";
                throw new InvalidOperationException(string.Format(emailTemplate, emailAddress, regexPattern), error);
            }

            return valid;
        }

        public bool AddMailMessage(MailMessage emailMessage)
        {
            bool added = false;
            try
            {
                _mailMessageList.Add(emailMessage);
                _currentMailMessage = emailMessage;
                added = true;
            }
            catch (Exception error)
            {
                throw new InvalidOperationException("Unable to add MailMessage to the list", error);
            }

            return added;
        }

        public bool SendAllMessages()
        {
            bool processed = false;
            
            try
            {
                if (_server != null)
                {
                    foreach (MailMessage mailItem in _mailMessageList)
                    {
                        try
                        {
                            _server.Send(mailItem);
                            _resultList.Add(new EmailerSuccessBase(true, mailItem));
                        }
                        catch (Exception error)
                        {
                            _resultList.Add(new EmailerSuccessBase(false, mailItem, error));
                        }
                    }

                    processed = true;
                }
                else
                {
                    throw new SmtpException("The smtp Server is not available");
                }
            }
            catch (Exception error)
            {
                throw new InvalidOperationException("Unable to send emails", error);
            }
            return processed;
        }

        public bool SendCurrentMessage()
        {
            bool valid = false;
            if (_server != null && _currentMailMessage != null)
            {
                _server.Send(_currentMailMessage);
                _resultList.Add(new EmailerSuccessBase(true, _currentMailMessage));
            }
            else
            {
                if (_server == null)
                {
                    throw new SmtpException("The smtp Server is not available");
                }
                else
                {
                    throw new ArgumentNullException("No Valid current email object selected");
                }
            }
            return valid;
        }


        public bool AttachFile(Stream objectToAttach, ContentType mimeType, string contentID)
        {
            bool valid = false; 
            try
            {
                Attachment itemToAttach = new Attachment(objectToAttach, mimeType);
                itemToAttach.ContentId = contentID;
                _currentMailMessage.Attachments.Add(itemToAttach);
            }
            catch (Exception error)
            {
                throw new ArgumentNullException(string.Format("Unable to attach the file {0} with content type of {1}", contentID, mimeType.Name), error);
            }

            return valid;
        }

        public bool ParseKeyValuePair(List<KeyValuePair<string, string>> parseValueList)
        {
            bool valid = false;
            string failedTemplate = "Failed to find insert point {{0}} with value {1}";
            StringBuilder failedParsing = new StringBuilder();
            try
            {
                foreach (KeyValuePair<string, string> parseItem in parseValueList)
                {
                    if (_currentMailMessage.Body.IndexOf(string.Format("{{0}}", parseItem.Key)) > -1)
                    {
                        _currentMailMessage.Body = _currentMailMessage.Body.Replace(string.Format("{{0}}", parseItem.Key), parseItem.Value);
                    }
                    else
                    {
                        failedParsing.AppendLine(string.Format(failedTemplate, parseItem.Key, parseItem.Value));
                    }
                }
                if (failedParsing.Length > 0)
                {
                    throw new MissingFieldException(failedParsing.ToString());
                }
                valid = true;
            }
            catch (Exception error)
            {
                throw new InvalidOperationException("Failed to parse values", error);
            }

            return valid; 
        }

        public EmailerBase(string serverName = null,int port = 0, string userName = "", string password = "")
        {
            try
            {
                if (string.IsNullOrEmpty(serverName))
                {
                    _server = new SmtpClient();
                }
                else if (string.IsNullOrEmpty(serverName) && port == 0)
                {
                    _server = new SmtpClient();
                }
                else if (!string.IsNullOrEmpty(serverName) && port == 0)
                {
                    _server = new SmtpClient(serverName);
                }
                else if (!string.IsNullOrEmpty(serverName) && port > 0)
                {
                    _server = new SmtpClient(serverName, port);
                }

                if (!string.IsNullOrEmpty(userName))
                {
                    _server.Credentials = new NetworkCredential(userName, password);
                }
                _fromAddress = null;
                _toAddressList = new List<MailAddress>();
                _ccAddressList = new List<MailAddress>();
                _bccAddressList = new List<MailAddress>();
                _mailMessageList = new List<MailMessage>();
                _resultList = new List<IEmailerSuccess>();
                _currentMailMessage = null;
            }
            catch (Exception error)
            {
                throw new InvalidOperationException("Unable to initialise EmailerBase", error);
            }
        }

    }
}
